#pragma once

#include <string>
#include <iostream>
#include <memory>
#include <dlfcn.h>
#include "Logger.h"

template <typename T>
class CPluginLoader
{
public:
	//���ﲻ��ʹ��RAII���ڹ������м���dll�������쳣�Ƚ��鷳 - ����������free dllһ����
	CPluginLoader(
		std::string path = "",
		std::string creator = "pluginCreator",
		std::string deleter = "pluginDeleter"
	)
		: hdll(NULL)
		, pluginPath(path)
		, pluginCreatorName(creator)
		, pluginDeleterName(deleter)
	{}
	~CPluginLoader() = default;

	int getPlugin(std::string path)
	{
		hdll = dlopen(path.c_str(), RTLD_NOW | RTLD_LAZY);
		if (hdll == NULL)
		{
			std::cout << "CPluginLoader::getPlugin -- fail to get plugin" << std::endl;
			LOGERROR("fail to get plugin");
			return 0;
		}

		return 1;
	}

	std::shared_ptr<T> getInstance()
	{
		//����Ҫע�⣬���ͱ����Ǻ���ָ�룬�����м��"(*)"һ����Ҫ����
		using PluginAllocatorFunctionType = T* (*)();
		using PluginDeleterFunctionType = void (*)(T*);

		if (hdll == NULL)
		{
			LOGERROR(dlerror());
			std::cout << dlerror() << std::endl;
			return nullptr;
		}

		PluginAllocatorFunctionType allocatorFunc = (PluginAllocatorFunctionType)dlsym(hdll, pluginCreatorName.c_str());
		PluginDeleterFunctionType deleterFunc = (PluginDeleterFunctionType)dlsym(hdll, pluginDeleterName.c_str());

		if (!allocatorFunc || !deleterFunc)
		{
			releasePlugin();
			LOGERROR(dlerror());
			std::cout << dlerror() << std::endl;
			return nullptr;
		}

		std::shared_ptr<T> dll_ptr(
			allocatorFunc(),
			[deleterFunc](T* ptr) {deleterFunc(ptr); });

		return dll_ptr;
	}

	int releasePlugin()
	{
		if (dlclose(hdll) == 0)
		{
			LOGERROR(dlerror());
			std::cout << dlerror() << std::endl;
			return 0;
		}
		return 1;
	}

private:
	void* hdll;
	std::string pluginPath;
	std::string pluginCreatorName;
	std::string pluginDeleterName;
};